package com.tim.helper.file;

import lombok.SneakyThrows;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description: xml中，非select标签
 * java中，insert、update、delete开头代码
 * @Version 1.0, 2021/5/21
 * @Author Tim
 */
public class FindCallFile {

  String searchPath = "E:\\gitlab\\exodus\\plat\\plat-pay";

    /**
     * 查询xml中涉及table的方法，再查询java中的调用
     *  xml中无table名称时，无法查询
     */
    @Test
    public void findXmlCall() throws IOException {
        String table = "goods_info";
        List<String> methods = Arrays.asList("update");// insert delete select update
        Map<String, Set<String>> map = new HashMap<>();// dao-methods记录
        String suffix = ".xml";
        Files.walkFileTree(Paths.get(searchPath), new SimpleFileVisitor<Path>() {
            @SneakyThrows
            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
                String fileName = path.getFileName().toString();
                String absolutePathStr = path.toAbsolutePath().toString();
//				System.out.println(fileName);
                // 遍历搜索路径；查询非target文件夹下xml
                if(!absolutePathStr.contains("\\target\\") && fileName.endsWith(suffix)) {
                    List<String> contents = Files.readAllLines(path);
                    String info = contents.stream().map(item->item+"\n").collect(Collectors.joining());
                    // 读取内容，如无table名称，则访问下一个文件
                    if(!info.contains(table)){
                        return FileVisitResult.CONTINUE;
                    }
                    // dom4j读取xml，记录dao-methods
                    SAXReader reader = new SAXReader();
                    Document document=null;
					try {
						document = reader.read(path.toFile());
					} catch (DocumentException e) {
						e.printStackTrace();
					}
                    Element rootElement = document.getRootElement();
                    boolean printPath = true;
                    String mapperClassName = null;
                    for(String method : methods){
                        // 遍历检索的方法标签
                        List<Element> eleList = rootElement.elements(method);
                        if(!CollectionUtils.isEmpty(eleList)){
                            for (Element ele : eleList) {
                                String text = ele.getText();// 转set，排除干扰
                                HashSet<String> hashSet = new HashSet<String>(Arrays.asList(text.split("\\s+")));
                                if(hashSet.contains(table)){
                                    if(printPath){
                                        System.out.println(path);
                                        printPath = false;
                                        mapperClassName = rootElement.attributeValue("namespace");
                                        mapperClassName = mapperClassName.substring(mapperClassName.lastIndexOf('.')+1).trim();
                                        if(!map.containsKey(mapperClassName)){
                                            map.put(mapperClassName, new HashSet<>());// 记录dao。重名时不重置
                                        }
                                    }
                                    String methodId = ele.attributeValue("id");
                                    map.get(mapperClassName).add(methodId);// 记录dao-methods
//                                    System.out.println(methodId);
                                }
                            }
                        }
                    }
                    return FileVisitResult.CONTINUE;
                }
                return FileVisitResult.CONTINUE;

            }

        });
        System.out.println("xml检索结果：");
        map.forEach((k, v)-> System.out.println(k+": "+v));
        System.out.println();
//        System.out.println(map);
        // 按dao-methods记录，检索调用
        for(Map.Entry<String, Set<String>> entry : map.entrySet()){
            System.out.println("调用 "+entry.getKey()+": "+entry.getValue());
            String mapperName = entry.getKey();
            StringBuilder builder = new StringBuilder("\\.(");
            boolean isFirst = true;
            for(String str : entry.getValue()){
            	if(isFirst){
            		isFirst = false;
            	}else{
            		builder.append("|");
            	}
                builder.append("(").append(str).append(")");
            }
            // 不拼接update等方法，会导致查询其他mapper的update调用
//            builder.append("|(")
//                    .append(methods.stream().collect(Collectors.joining(")|(")))
//                    .append(")")
//                    .append(")");
            builder.append(")");
            doFindJava(mapperName, builder.toString());
        }
    }

    @Test
    public void findJavaCall() throws IOException {
        String mapperName = "RefundDAO";
        String methods = "(insert)|(delete1)|(select1)|(update1)|(batch)";// (insert) |(delete) |(select) |(update)
        // 移除后缀为1的方法
         methods = Arrays.stream(methods.split("\\|"))
                 .filter(item -> !item.contains("1"))
                 .collect(Collectors.joining("|"));
        String findReg = "\\.("+methods+")";
        doFindJava(mapperName, findReg);
    }

    // 查找调用mapper指定方法的java类
    private void doFindJava(String className, String findReg) throws IOException {
        String suffix = ".java";
        Files.walkFileTree(Paths.get(searchPath), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
                String fileName = path.getFileName().toString();
                String absolutePathStr = path.toAbsolutePath().toString();
//				System.out.println(fileName);
                if(!absolutePathStr.contains("\\target\\") && fileName.endsWith(suffix)) {
                    try {
                        List<String> contents = Files.readAllLines(path);
                        String varName = null;
                        String mapperVarReg = className +"\\s+(\\w+)";
                        Pattern varNamePattern = Pattern.compile(mapperVarReg);
                        Pattern callPattern = null;

                        for (int i = 0; i < contents.size(); i++) {
                            String item = contents.get(i).trim();
                            // 可能被优化为导入*
//                            if(!hasImport && item.contains("."+ className)){
//                                // 有导入
//                                hasImport = true;
//                                continue;
//                            }
//                            if(item.contains("public ") && !hasImport) {
//                                // 无导入，返回
//                                break;
//                            }
                            if(varName==null && item.contains(className)){
                                // 找出变量名
                                Matcher matcher = varNamePattern.matcher(item);
                                if(matcher.find()){
                                    varName = matcher.group(1);
                                    String callRegex = varName+ findReg;
                                    callPattern = Pattern.compile(callRegex);
                                }
                            }
                            if(varName!=null && item.contains(varName)){
                                // 包含指定方法，输出类名
                                if(callPattern.matcher(item).find()){
                                    System.out.println(path);
                                    break;
                                }
                            }
                        }
                    }catch (Exception e) {
                        e.printStackTrace();
                        System.out.println(path);
                    }

                }
                return FileVisitResult.CONTINUE;
            }

        });
    }

    long start;
    @Before
    public void before(){
        start = System.currentTimeMillis();
    }
    @After
    public void after(){
        System.out.println("总耗时："+(System.currentTimeMillis()-start)+"ms");
    }

  public static void main(String[] args) {
    System.out.println("a.changeShopGoodsCategory".matches("a\\.((changeShopGoodsCategory))"));
  }
}
